﻿using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

namespace HF
{
    public static partial class HExpr
    {
        /// <summary>
        /// 通过 <see cref="Expression"/> 编译创建实例的构造匿名函数
        /// </summary>
        public static class NewOBJ
        {
            /// <summary>
            /// 无参构造缓存
            /// </summary>
            private static readonly ConcurrentDictionary<Type, Lazy<(Func<object>, Delegate)>> _Map = new();

            /// <summary>
            /// 有参构造
            /// </summary>
            private static readonly ConcurrentDictionary<(Type, Type[]), Lazy<(Func<object[], object>, Delegate)>> _MapArgs = new();

            /// <summary>
            /// 通过 <see cref="Expression"/> 构建新的实例
            /// </summary>
            /// <param name="type">实例类型</param>
            /// <returns></returns>
            public static void Complice(Type type, out Func<object> create, out Delegate fastCreate)
            {
                type.CheckArgumentNull(nameof(type));
                var lazy = _Map.GetOrAdd(type, v => newLazy(v));
                create = lazy.Value.Item1;
                fastCreate = lazy.Value.Item2;

                Lazy<(Func<object>, Delegate)> newLazy(Type t)
                {
                    var result = new Lazy<(Func<object>, Delegate)>(() =>
                    {
                        Expression expr = Expression.New(t);
                        var methodName = $"Fun<{t.GetTypeFullName()}>";
                        var lambda2 = Expression.Lambda(expr, methodName, false, null);
                        var r2 = lambda2.Compile();

                        if (t.IsValueType)
                        {
                            expr = Expression.Convert(expr, typeof(object));
                        }

                        var lamdba1 = Expression.Lambda<Func<object>>(expr);
                        var r1 = lamdba1.Compile();
                        return (r1, r2);
                    }
                    , LazyThreadSafetyMode.ExecutionAndPublication);
                    return result;
                }
            }

            /// <summary>
            /// 通过 <see cref="Expression"/> 构建新的实例
            /// </summary>
            /// <param name="type">实例类型</param>
            /// <param name="args">完整的参数,类型必须正确</param>
            /// <returns></returns>
            public static void Complice(Type type, Type[] args, out Func<object[],object> create, out Delegate fastCreate)
            {
                const BindingFlags C_BF = C_DEF_BF | BindingFlags.Instance;
                type.CheckArgumentNull(nameof(type));
                type.CheckArgumentNull(nameof(args));
                (args.Length == 0).CheckArgumentOutOfRange(nameof(args));
                if (!_MapArgs.TryGetValue((type, args), out var lazy))
                {
                    var coninfo = type.GetConstructor(C_BF,null,args,null);
                    if (coninfo == null)
                    {
                        var ts = args.Select(v => v.GetTypeFullName()).Aggregate((u, d) => $"{u}, {d}");
                        var msg = $"未能从类型 {type.GetTypeFullName()} 查询到参数类型为 [{ts}] 的构造函数。";
                        new ArgumentException(msg, nameof(type)).Throw();
                    }

                    lazy = _MapArgs.GetOrAdd((type, args), v => newLazy(v));
                }

                create = lazy.Value.Item1;
                fastCreate = lazy.Value.Item2;

                Lazy<(Func<object[], object>, Delegate)> newLazy((Type, Type[]) t)
                {
                    var result = new Lazy<(Func<object[], object>, Delegate)>(() =>
                    {
                        var coninfo = t.Item1.GetConstructor(C_BF, null, t.Item2, null);
                        Func<object[], object> r1;
                        Delegate r2;
                        {
                            var ep = Expression.Parameter(typeof(object[]), "param");
                            var ec = t.Item2.Select((v, i) =>
                                Expression.Convert(
                                    Expression.ArrayAccess(ep, Expression.Constant(i)), v));
                            Expression expr = Expression.New(coninfo, ec);

                            //如果是值类型就强转 Object
                            if (t.Item1.IsValueType)
                            {
                                expr = Expression.Convert(expr, typeof(object));
                            }

                            var lamdba = Expression.Lambda<Func<object[], object>>(expr, ep);
                            r1 = lamdba.Compile();
                        }

                        {
                            var ep = t.Item2.Select((v, i) => Expression.Parameter(v, "param" + i)).ToArray();
                            var method = t.Item2
                                .Select(v => v.GetTypeFullName())
                                .Aggregate((u, d) => $"{u}, {d}");

                            Expression expr = Expression.New(coninfo, ep);
                            var lamdba = Expression.Lambda(expr, $"Fun<{method}, {t.Item1.GetTypeFullName()}>", false, ep);
                            r2 = lamdba.Compile();
                        }

                        return (r1, r2);
                    }
                    , LazyThreadSafetyMode.ExecutionAndPublication);
                    return result;
                }
            }
        }

    }
}
